#include <func.h>

typedef struct train_s{
    int dataLen;
    char buf[1024];
} train_t;

//循环传输接收
int recvn(int sockfd, void *pstart, int len){
    int total = 0;
    int ret;
    char *p = (char*)pstart;
    while (total < len){
        ret = recv(sockfd, p+total, len-total, 0);
        if (ret == 0){
            return -1;
        }
        total += ret;
    }
    return 0;
}

int download(int sockfd){
    char fileName[1024] = {0};
    int dataLen;
    recvn(sockfd, &dataLen, sizeof(int));
    if (dataLen == 0){
        printf("filename error\n");
        return -1;
    }
    recvn(sockfd, fileName, dataLen);

    //在download文件夹下创建文件
    char downloadPath[1024] = {0};
    sprintf(downloadPath, "%s/%s", "download", fileName);
    int fd = open(downloadPath, O_RDWR | O_CREAT, 0666);
    ERROR_CHECK(fd, -1, "open");

    int fileSize;
    recvn(sockfd, &dataLen, sizeof(int));
    recvn(sockfd, &fileSize, dataLen);

    //客户端实现断点续传，将文件断点信息发送给服务端，并移动偏移
    struct stat statbuf;
    int ret = fstat(fd, &statbuf);
    ERROR_CHECK(ret, -1, "fstat");
    int exitSize = statbuf.st_size;
    send(sockfd, &exitSize, sizeof(exitSize), MSG_NOSIGNAL);
    lseek(fd, 0, SEEK_END);

    time_t timeBeg, timeEnd;
    char buf[4096] = {0};

    timeBeg = time(NULL);
    if(fileSize >= 100<<20)
    {
        ftruncate(fd, fileSize); // 预设文件空洞(直接忽略原本文件已经存在的情况)
        char *p = (char *)mmap(NULL, fileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        ERROR_CHECK(p, MAP_FAILED, "mmap");
        recvn(sockfd, p, fileSize);
        munmap(p, fileSize);
        timeEnd = time(NULL);
    }
    else 
    {
        while (1)
        {
            bzero(buf, sizeof(buf));
            ret = recv(sockfd, &dataLen, sizeof(int), 0);
            if (dataLen == 0)
            {
                timeEnd = time(NULL);
                break;
            }
            if (ret == 0)
            {
                close(fd);
                return -1;
            }
            recvn(sockfd, buf, dataLen);
            write(fd, buf, dataLen);
        }
    }
    close(fd);
    int total_time = timeEnd - timeBeg;
    printf("download done, total time = %ds\n", total_time);
    return 0;
}


int upload(int sockfd, const char* pathName){
    //暂时只支持绝对路径上传
    printf("send file: %s\n", pathName);
    train_t train;
    int ret;
    
    char* filename = basename(pathName);
    
    train.dataLen = strlen(filename);
    strcpy(train.buf, filename);
    send(sockfd, &train, train.dataLen + 4, 0);

    struct stat buf;
    int fd = open(pathName, O_RDWR);
    fstat(fd, &buf);
    train.dataLen = sizeof(buf.st_size);
    memcpy(train.buf, &buf.st_size, train.dataLen);
    send(sockfd, &train, train.dataLen + 4, 0);

    if (buf.st_size < (100 << 20)) {
        printf("send small file\n");
        while ((train.dataLen = read(fd, train.buf, sizeof(train.buf)))) {
            ret = send(sockfd, &train, 4 + train.dataLen, 0);
            ERROR_CHECK(ret, -1, "send_small_train");
        }
        send(sockfd, &train, 4, 0);
    } else {
        printf("send big file\n");
        char* pMap = (char*)mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        ERROR_CHECK(pMap, (char*)-1, "mmap");
        ret = send(sockfd, pMap, buf.st_size, 0);
        ERROR_CHECK(ret, -1, "send_mmap");
    }
    close(fd);
    return 0;
}
